#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
reload(sys)
sys.setdefaultencoding('utf-8')
import os
import imp
import urllib
from urlparse import urlparse
import re

#import sip
#sip.setapi('QVariant', 2) # auto-convert types ON
#sip.setapi('QString', 2)

from PyQt4 import QtGui, QtCore, QtNetwork
from PyQt4.QtCore import SIGNAL#, QString


RE_ENC = re.compile(r'<meta\s+http\-equiv\s*=\s*"content\-type"\s+content\s*=\s*".*?;\s+charset=(.*?)"', re.I|re.S)

VERSION = '0.0.0.1'
QT_APPLICATION = 'settings'
QT_COMPANY = 'AV/Photo Downloader'

APP_PATH = os.path.dirname(unicode(__file__, sys.getfilesystemencoding())) if not hasattr(sys, 'frozen') \
                else os.path.dirname(unicode(sys.executable, sys.getfilesystemencoding()))

ICO_PATH = os.path.join(APP_PATH, 'icons')
PLUGIN_PATH = [os.path.join(APP_PATH, 'plugins')]

class App(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(App, self).__init__(parent)

        self.ini = QtCore.QSettings(QtCore.QSettings.IniFormat,
                    QtCore.QSettings.UserScope, QT_COMPANY, QT_APPLICATION)

        self.icons = {}
        self.store_dir = unicode(self.ini.value('global/store_dir', ".").toString())
        #self.initProxy()

        self.loadPlugins()

        self.initActions()
        self.initMenu()
        self.initToolBar()
        self.initStatusBar()

        self.initDock()

        self.loadGeometry()

        self.setWindowTitle(u"Фото-качалка")
        self.setWindowIcon(self.ico("", "py.png"))


    def ico(self,themename, filename):
        """Qt4.6+ workaround for loading themed icons"""

        try:
            if themename and QtGui.QIcon.hasThemeIcon(themename):
                i = QtGui.QIcon.fromTheme(themename)#, QtGui.QIcon(os.path.join(ICO_PATH,filename))) # Qt 4.6+
            else:
                if filename.startswith(":"):
                    i = QtGui.QIcon(filename)
                else:
                    i = QtGui.QIcon(os.path.join(ICO_PATH,filename))
        except:
            if filename.startswith(":"):
                    i = QtGui.QIcon(filename)
            else:
                i = QtGui.QIcon(os.path.join(ICO_PATH,filename))

        return i

    def toUnicode(self, s):
        r = RE_ENC.search(s)
        if r:
            return unicode(s, r.group(1))
        else:
            return unicode(s, 'utf-8')

    def loadPlugins(self):
        self.plugins = []
        tmp = []
        for d in PLUGIN_PATH:
            if not os.path.exists(d):
                continue
            for f in os.listdir(d):
                fn = os.path.join(d, f)
                if not os.path.isdir(fn) and not f.startswith('__'):
                    try:
                        f0 = os.path.splitext(f)[0]
                        if f0.lower() in tmp:
                            continue
                        tmp.append(f0.lower())
                        mm = imp.find_module(f0, [d])
                        m = imp.load_module(f0, *mm)
                        if hasattr(m, 'NAME') and hasattr(m, 'TITLE'):
                            self.plugins.append(m)
                    except Exception as e:
                        print "LoadPlug err: %s" % (e,)
        tmp = None

    def loadGeometry(self):
        try:
            geo = self.ini.value("global/geometry").toPyObject()
            #self.restoreGeometry(.toByteArray())
            if geo:
                self.setGeometry(geo)
        except AttributeError:
            pass
        try:
            self.resotreState(self.ini.value("global/state").toByteArray())
        except AttributeError:
            pass

        m = self.ini.value("global/maximized", "false").toBool()
        if m:
            self.showMaximized()


    def initActions(self):
        self.ac_quit = QtGui.QAction(self.ico("system-log-out", "system-log-out.png"), u"Вы&ход", self,
                        shortcut="Ctrl+Q", statusTip=u"Выйти из программы", triggered=self.close)

        self.ac_getAlbums = QtGui.QAction(self.ico("view-refresh","view-refresh.png"), u"Альбомы", self,
                            statusTip=u"Загрузить список альбомов", triggered=self.onGetAlbumList)

        self.ac_getSelected = QtGui.QAction(self.ico("document-save","document-save.png"), u"Скачать", self,
                            statusTip=u"Загрузить выделенные фотографии", triggered=self.onLoadSelected)


        self.ac_delUser = QtGui.QAction(u"Удалить", self,
                            statusTip=u"Удалить из списка", triggered=self.onDelUser)


    def initMenu(self):
        self.mb = self.menuBar()
        menuFile = self.mb.addMenu(u"&Файл")
        menuFile.addAction(self.ac_quit)

        menuEdit = self.mb.addMenu(u"&Правка")
        menuEdit.addAction(self.ac_getAlbums)
        menuEdit.addAction(self.ac_getSelected)
        menuEdit.addSeparator()
        self.menuView = self.mb.addMenu(u"Вид")
        m = self.mb.addMenu(u"Справка")

    def initStatusBar(self):
        self.statusBar().showMessage(u"Готово")

    def initToolBar(self):
        tb = self.addToolBar("File")
        tb.setObjectName('file')
        tb.addAction(self.ac_quit)

        # add plugins
        self.plug_cb = QtGui.QComboBox()
        self.plug_cb.setDuplicatesEnabled(False)
        self.plug_cb.setEditable(False)
        for p in self.plugins:
            self.plug_cb.addItem(p.TITLE, QtCore.QVariant(p))

        self.plug_cb.currentIndexChanged.connect(self.onPluginChanged)

        tb = self.addToolBar('Actions')
        tb.setObjectName('actions')
        lb = QtGui.QLabel(u"Сервис:  ")
        lb.setBuddy(self.plug_cb)
        tb.addWidget(lb)
        tb.addWidget(self.plug_cb)

        tb.addSeparator()

        # add login combo
        self.user_cb = QtGui.QComboBox()
        self.user_cb.setDuplicatesEnabled(False)
        self.user_cb.setEditable(True)
        self.user_cb.setInsertPolicy(QtGui.QComboBox.InsertAlphabetically)
        self.user_cb.setMaxCount(50)
        self.user_cb.currentIndexChanged.connect(self.onLoginChanged)
        self.user_cb.addAction(self.ac_delUser)
        self.user_cb.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
        #self.user_cb.contextMenuEvent
        lb = QtGui.QLabel(u"Пользователь:  ")
        lb.setBuddy(self.user_cb)
        tb.addWidget(lb)
        tb.addWidget(self.user_cb)

        tb.addSeparator()
        # buttons
        tb.addAction(self.ac_getAlbums)
        tb.addAction(self.ac_getSelected)


        # set current plugin
        s = unicode(self.ini.value('global/plugin', u'').toString())
        if s:
            pp = [x for x in self.plugins if x.NAME==s][0]
            i = self.plug_cb.findText(pp.TITLE)
            if i >= 0:
                self.plug_cb.setCurrentIndex(i)

        # force update login list
        self.onPluginChanged(self.plug_cb.currentIndex())


    def initDock(self):
        self.setDockNestingEnabled(True)
        dock = QtGui.QDockWidget(u"Альбомы", self)
        dock.setObjectName('albums')
        dock.setAllowedAreas(QtCore.Qt.LeftDockWidgetArea|QtCore.Qt.RightDockWidgetArea)#|QtCore.Qt.TopDockWidgetArea|QtCore.Qt.BottomDockWidgetArea)
        self.albums = QtGui.QListWidget(dock)
        self.albums.setSortingEnabled(True)
        self.albums.itemDoubleClicked.connect(self.onAlbumDbl)
        dock.setWidget(self.albums)
        self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, dock)
        self.menuView.addAction(dock.toggleViewAction())
        self.d1 = dock

        dock = QtGui.QDockWidget(u"Фотографии", self)
        dock.setObjectName('photos')
        dock.setAllowedAreas(QtCore.Qt.LeftDockWidgetArea|QtCore.Qt.RightDockWidgetArea)#|QtCore.Qt.TopDockWidgetArea|QtCore.Qt.BottomDockWidgetArea)
        self.photos = QtGui.QListWidget(dock)
        self.photos.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
        self.photos.setSortingEnabled(True)
        dock.setWidget(self.photos)
        self.addDockWidget(QtCore.Qt.RightDockWidgetArea, dock)
        self.menuView.addAction(dock.toggleViewAction())
        self.d2 = dock


    # --- Signal handlers ----

    def onPluginChanged(self, index):
        plg = self.plug_cb.itemData(index).toPyObject()
        if not plg:
            return

        self.ini.setValue('global/plugi', QtCore.QVariant(plg.NAME))

        ls = self.ini.value(u'plugins/%s/users' % (plg.NAME,), QtCore.QVariant([])).toPyObject()
        cur = unicode(self.ini.value(u'plugins/%s/current_user' % (plg.NAME,), '').toString())
        self.user_cb.clear()
        if ls: self.user_cb.addItems(ls)
        if cur:
            i = self.user_cb.findText(cur)
            if i >= 0:
                self.user_cb.setCurrentIndex(i)

    def onLoginChanged(self, index):
        plg = self.plug_cb.itemData(self.plug_cb.currentIndex()).toPyObject()
        cur = self.user_cb.currentText()
        ls = [self.user_cb.itemText(i) for i in xrange(self.user_cb.count())]
        if not ls and cur:
            ls.append(cur)
        self.ini.setValue(u'plugins/%s/users' % (plg.NAME,), QtCore.QVariant(ls))
        self.ini.setValue(u'plugins/%s/current_user' % (plg.NAME,), QtCore.QVariant(cur))

    def onDelUser(self):
        """Delete item from users combo box"""
        cur = self.user_cb.currentText()
        curi = self.user_cb.currentIndex()
        if curi >= 0:
            if QtGui.QMessageBox.question(self, "", u"Удадить %s?" % (cur,)):
                self.user_cb.removeItem(curi)
                print "DELETLED"
                self.onLoginChanged(self.user_cb.currentIndex())


    def onGetAlbumList(self):
        plg = self.plug_cb.itemData(self.plug_cb.currentIndex()).toPyObject()
        cur = unicode(self.user_cb.currentText())
        if not cur:
            return

        ls = [self.user_cb.itemText(i) for i in xrange(self.user_cb.count())]
        if cur not in ls:
            self.user_cb.addItem(cur)
            ls.append(cur)
            self.ini.setValue(u'plugins/%s/users' % (plg.NAME,), QtCore.QVariant(ls))
            self.ini.setValue(u'plugins/%s/current_user' % (plg.NAME,), QtCore.QVariant(cur))

        h = plg.album_list_uri(unicode(cur))
        dlg = QtGui.QProgressDialog(u"Получаем список альбомов",u"Отменить",0,1,self)
        dlg.setWindowModality(QtCore.Qt.WindowModal)
        dlg.setWindowTitle(u" ")
        dlg.setValue(0)
        dlg.show()
        print u"GET ALBUMS on %s for %s\nURL=%s" % (plg.NAME, cur, h['url'])
        self.statusBar().showMessage(u"Загружаем список альбомов...")
        self.albums.clear()
        ret = h
        try:
            go = True
            url = h['url']
            while go and url:
                QtGui.qApp.processEvents()
                print "Get: ", url
                f = urllib.urlopen(url)
                s = f.read()
                f.close()
                ret = plg.parse_album_list(s, **ret)
                url = ret.get('next_page','')
                if url:
                    dlg.setMaximum(dlg.maximum()+1)
                else:
                    go = False
                dlg.setValue(dlg.value()+1)
                if dlg.wasCanceled():
                    go = False

                for href, title, cnt in ret['albums']:
                    itm = QtGui.QListWidgetItem(u"%s%s" % (title, u" [%s]" % (cnt,) if cnt else u""))
                    itm.setData(QtCore.Qt.UserRole, href)
                    self.albums.addItem(itm)

                QtGui.qApp.processEvents()

        except Exception, e:
            print u"EXC: %s", (e,)
        finally:
            dlg.close()
            self.statusBar().showMessage(u"Готово.")

    # --- networking handlers ---

    def _do_request(self, url, on_done):
        r = QtNetwork.QNetworkRequest(QtCore.QUrl(url))


    def onAlbumDbl(self, album):
        plg = self.plug_cb.itemData(self.plug_cb.currentIndex()).toPyObject()
        cur = unicode(self.user_cb.currentText())
        if not cur:
            return
        data = unicode(album.data(QtCore.Qt.UserRole).toPyObject(),'utf-8')
        #print "Double!", data
        self.photos.clear()
        dlg = QtGui.QProgressDialog(u"Получаем список фотографий",u"Отменить",0,1,self)
        dlg.setWindowModality(QtCore.Qt.WindowModal)
        dlg.setWindowTitle(u" ")
        dlg.setValue(0)
        dlg.show()
        ret = {}
        try:
            go = True
            while go and data:
                QtGui.qApp.processEvents()
                f = urllib.urlopen(data)
                s = f.read()
                f.close()
                ret = plg.parse_photo_list(s, user=cur, **ret)
                if 'user' in ret: ret.pop('user')
                pcnt = ret.get('page_count', 1)
                next = ret.get('next_page', '')
                if pcnt and dlg.maximum() != pcnt:
                    dlg.setMaximum(pcnt)
                elif next:
                    dlg.setMaximum(dlg.maximum()+1)
                else:
                    go = False
                if dlg.wasCanceled():
                    go = False
                data = next
                dlg.setValue(dlg.value()+1)
                QtGui.qApp.processEvents()

                for p in ret["photos"]:
                    itm = QtGui.QListWidgetItem(p['title'])
                    itm.setData(QtCore.Qt.UserRole, p)
                    self.photos.addItem(itm)

        except Exception, e:
            print "GetPhoto list Exc: %s" % (e,)
        finally:
            dlg.close()
            self.statusBar().showMessage(u"Готово.")

        #self.reqB = QtNetwork.QNetworkRequest(QtCore.QUrl(data))
        #self.phalb_cnt = 0
        #self.net_phlist.get(self.reqB)

    def onNetPhotoErr(self, err):
        print "ERR: %s", (err.errorString(),)

    def onLoadSelected(self):
        s = QtGui.QFileDialog.getExistingDirectory(self, u"Папка для сохранения", self.store_dir)
        print "StoreDir:", s
        if not s:
            return
        self.store_dir = unicode(s)
        self.ini.setValue('global/store_dir', QtCore.QVariant(self.store_dir))

        photos = [self.photos.item(i) for i in xrange(self.photos.count()) if self.photos.isItemSelected(self.photos.item(i))]
        dlg = QtGui.QProgressDialog(u"Получаем список фотографий",u"Отменить",0,len(photos),self)
        dlg.setWindowModality(QtCore.Qt.WindowModal)
        dlg.setWindowTitle(u" ")
        dlg.setValue(0)
        dlg.show()
        y_to_all = False
        n_to_all = False
        try:
            for i, itm in enumerate(photos):
                try:
                    if dlg.wasCanceled():
                        break
                    d = itm.data(QtCore.Qt.UserRole).toPyObject()
                    dlg.setLabelText(u"Загружаем %s" % (d[QtCore.QString('title')]))
                    dlg.setValue(i)
                    QtGui.qApp.processEvents()
                    nm = unicode(d[QtCore.QString('title')])
                    if not nm.lower().endswith('.jpg'):
                        nm = os.path.basename(unicode(d[QtCore.QString('full')]))
                        if not nm.lower().endswith('.jpg'):
                            nm += '.jpg'
                    f = urllib.urlopen(unicode(d[QtCore.QString('full')]))
                    s = f.read()
                    f.close()
                    fn = os.path.join(self.store_dir, nm)
                    if os.path.exists(fn):
                        if n_to_all:
                            continue
                        if y_to_all:
                            pass
                        else:
                            sz = os.stat(fn).st_size
                            sz1 = len(s)
                            if sz != sz1:
                                r = QtGui.QMessageBox.question(self, u"Файл уже есть",
                                    u"Файл '%s' уже существует, но имеет другой размер.\nСтарый: %s\nНовый: %s\nПерезаписать?" %\
                                    (fn, sz, sz1), QtGui.QMessageBox.Yes | QtGui.QMessageBox.YesToAll |\
                                    QtGui.QMessageBox.No | QtGui.QMessageBox.NoToAll)
                                if r == QtGui.QMessageBox.YesToAll:
                                    y_to_all = True
                                elif r == QtGui.QMessageBox.NoToAll:
                                    n_to_all = True
                                    continue
                                elif r == QtGui.QMessageBox.Yes:
                                    pass
                                else:
                                    continue
                                with open(fn, "wb") as f:
                                    f.write(s)
                            else:
                                continue
                    else:
                        with open(fn, "wb") as f:
                                f.write(s)

                except Exception, e:
                    print "Download fail: %s" % (e,)
                    pass
        except Exception, e:
            print "GetPhoto Exc: %s" % (e,)
        finally:
            dlg.hide()
            dlg.close()
            self.statusBar().showMessage(u"Готово.")

        return


    def closeEvent(self, event):
        """aboutToQuit signal handler"""
        self.ini.setValue("global/maximized", QtCore.QVariant(self.isMaximized()))
        try:
            self.ini.setValue("global/geometry", QtCore.QVariant(self.normalGeometry()))
        except:
            pass
        try:
            self.ini.setValue("global/state", QtCore.QVariant(self.saveState()))
        except:
            pass

        event.accept()



if __name__ == "__main__":
    if os.name == "nt":
        PLUGIN_PATH.append(os.path.normpath(os.path.join(os.environ['APPDATA'], QT_COMPANY, 'Plugins')))
    app = QtGui.QApplication(sys.argv)
    w = App()
    w.show()
    w.loadGeometry()
    app.exec_()
